package test.serverframe.armc.server.manager.service;

import com.microcore.dao.mapper.InvokeTriggerMapper;
import com.microcore.entity.InvokeTrigger;
import com.microcore.entity.param.TriggerParams;
import com.microcore.entity.UpdateTrigger;
import com.microcore.service.AssertService;
import com.microcore.service.JobService;
import com.microcore.service.TriggerService;
import com.microcore.util.DateUtil;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.utils.Key;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import test.serverframe.armc.server.manager.controller.vo.IntegrationTestAdd;
import test.serverframe.armc.server.manager.controller.vo.IntegrationTestModify;
import test.serverframe.armc.server.manager.controller.vo.IntegrationTestModifyStatus;
import test.serverframe.armc.server.manager.controller.vo.IntegrationTestTriggerParams;
import test.serverframe.armc.server.manager.dao.mapper.ExecutionPlanMapper;
import test.serverframe.armc.server.manager.dao.mapper.IntegrationTestMapper;
import test.serverframe.armc.server.manager.domain.ExecutionPlan;
import test.serverframe.armc.server.manager.domain.IntegrationTest;
import test.serverframe.armc.server.manager.domain.IntegrationTestInterface;
import test.serverframe.armc.server.manager.service.exec.IntegrationTester;
import test.serverframe.armc.server.manager.service.exec.params.IntegrationTestParams;

import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

import static test.serverframe.armc.server.util.RestTemplateUtil.getParams;

/**
 * 集成测试
 *
 * @author LeiZhenYang
 * @date 2018.11.29
 */
@Service("integrationTestService")
public class IntegrationTestService extends BaseService<IntegrationTest, String> {

    /**
     * 执行计划使用的class名称
     */
    public static final String CLASS_NAME = "integrationTestService";

    /**
     * 执行计划反射的方法名称
     */
    public static final String METHOD_NAME = "execute";

    public static final String SELF_ID = "#SELF_ID#";
    public static final String EXECUTE_COUNT = "#EXECUTE_COUNT#";
    public static final String CONCURRENT_COUNT = "#CONCURRENT_COUNT#";

    public static final int TEST_WAITING = 0;
    public static final int TEST_EXECUTING = 1;
    public static final int TEST_PAUSING = 2;

    @Autowired
    private IntegrationTestMapper mapper;

    @Autowired
    private InvokeTriggerMapper triggerMapper;

    @Autowired
    private ExecutionPlanMapper executionPlanMapper;

    @Autowired
    private SchedulerFactoryBean factory;

    @Autowired
    private TriggerService triggerService;

    @Autowired
    private IntegrationTestInterfaceService interfaceService;

    @Autowired
    @Qualifier("mcJobService")
    private JobService jobService;

    @Override
    protected String getMapperName() {
        return IntegrationTestMapper.class.getName();
    }

    /**
     * 新增集成测试
     *
     * @param record
     * @return
     */
    @Override
    public int insertSelective(IntegrationTest record) {
        record.setCreateTime(new Date());
        record.setUpdateTime(new Date());
        return super.insertSelective(record);
    }

    /**
     * 添加集成测试
     *
     * @param record
     * @return
     */
    @Transactional
    public boolean addIntegrationTest(IntegrationTestAdd record) throws Exception {
        IntegrationTest it = new IntegrationTest(record);
        it.setUsed(true);
        it.setClassName(CLASS_NAME);
        it.setMethodName(METHOD_NAME);
        it.setStatus(TEST_WAITING);
        // 执行参数模板
        it.setParams("[{\"key\":\"id\",\"value\":\"" + SELF_ID + "\"},{\"key\":\"executeCount\",\"value\":" + EXECUTE_COUNT + "},{\"key\":\"concurrentCount\",\"value\":" + CONCURRENT_COUNT + "}]");
        String id = UUID.randomUUID().toString();
        it.setId(id);
        // FIXME 暂时默认为true
        it.setConcurrent(true);
        insertSelective(it);
        if (record.getInterfaces() != null) {
            record.getInterfaces().forEach(inter -> {
                inter.setItId(id);
                interfaceService.addIntegrationTestInterface(inter);
            });
        }
        // 保存调度计划
        addTriggers(id, record, it.getParams());
        return true;
    }

    /**
     * 新增执行计划的调度trigger
     *
     * @param itId
     * @param record
     * @param paramTemplate
     * @throws Exception
     */
    public void addTriggers(String itId, IntegrationTestAdd record, String paramTemplate) throws Exception {
    	if (record.getIntegrationTestTriggerParams() != null) {
    		for (IntegrationTestTriggerParams trigger : record.getIntegrationTestTriggerParams()) {
	            // 替换执行参数为当前计划执行参数
	            String executeParam = paramTemplate.replace(SELF_ID, itId).
	                    replace(EXECUTE_COUNT, String.valueOf(trigger.getExecuteCount())).
	                    replace(CONCURRENT_COUNT, String.valueOf(trigger.getConcurrentCount()));
	            String triggerId = triggerService.addTrigger(trigger, itId, executeParam);
	            addExecutionPlan(trigger, triggerId);
    		}
    	}
        
    }

    /**
     * 保存执行计划
     *
     * @param trigger
     * @param triggerId
     */
    public void addExecutionPlan(IntegrationTestTriggerParams trigger, String triggerId) {
        ExecutionPlan plan = new ExecutionPlan();
        plan.setId(UUID.randomUUID().toString());
        plan.setTriggerId(triggerId);
        plan.setConcurrentCount(trigger.getConcurrentCount());
        plan.setExecuteCount(trigger.getExecuteCount());
        executionPlanMapper.insert(plan);
    }

    //@Transactional
    public void batchUpdateStatus(List<IntegrationTestModifyStatus> paramList) throws Exception {

        Scheduler scheduler = factory.getScheduler();
        for (IntegrationTestModifyStatus params : paramList) {
            modifyStatus(params.getStatus(), params.getId());

            List<InvokeTrigger> triggers = triggerMapper.getTriggersByInvokeId(params.getId());
            for (InvokeTrigger trigger : triggers) {
                JobKey jobKey = JobKey.jobKey(trigger.getInvokeTriggerId(), Key.DEFAULT_GROUP);
                if (params.getStatus() == TEST_WAITING) {
                    IntegrationTest concurrentInvoke = mapper.selectByPrimaryKey(params.getId());
                    jobService.startInvoke(scheduler, concurrentInvoke.getClassName(), concurrentInvoke.getMethodName(),
                            concurrentInvoke.getConcurrent(), false, concurrentInvoke.getId());
                    modifyStatus(TEST_EXECUTING, params.getId());

                } else if (params.getStatus() == TEST_EXECUTING) {
                    scheduler.pauseJob(jobKey);
                    modifyStatus(TEST_PAUSING, params.getId());

                } else if (params.getStatus() == TEST_PAUSING) {
                    scheduler.resumeJob(jobKey);
                    modifyStatus(TEST_EXECUTING, params.getId());
                }
            }
        }
    }

    private void modifyStatus(int status, String id) {
        IntegrationTest it = new IntegrationTest();
        it.setStatus(status);
        it.setId(id);
        mapper.updateByPrimaryKeySelective(it);
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean modifyIntegrationTest(IntegrationTestModify record) throws Exception {
        IntegrationTest it = new IntegrationTest(record);
        String id = record.getId();
        it.setId(id);
        // 删除集成测试接口
        interfaceService.deleteIntegrationTestInterfaceByItId(id);
        if (record.getInterfaces() != null) {
            record.getInterfaces().forEach(inter -> {
                inter.setItId(id);
                interfaceService.addIntegrationTestInterface(inter);
            });
        }
        //修改集成测试
        updateSelective(it);
        //更新Trigger
        updateTrigger(record);
        return true;
    }

    /**
     * 删除集成测试
     *
     * @param id
     * @return
     */
    @Transactional
    public boolean deleteIntegrationTest(String id) {
        interfaceService.deleteIntegrationTestInterfaceByItId(id);
        deleteByPrimaryKey(id);
        return true;
    }

    /**
     * 修改集成测试
     *
     * @param record
     * @return
     */
    @Transactional
    public int  updateSelective(IntegrationTest record) {
        record.setCreateTime(null);
        record.setUpdateTime(new Date());
        return super.updateByPrimaryKeySelective(record);
    }

    @Autowired
    private IntegrationTestRecordService recordService;

    @Autowired
    private IntegrationTestInterfaceRecordService interfaceRecordService;

    @Autowired
    private IntegrationTestInterfaceUseCaseRecordAssoService recordAssoService;

    @Autowired
    private InterfaceTestService interfaceTestService;

    @Autowired
    private TestUseCaseRecordService testUseCaseRecordService;

    @Autowired
    private AssertService assertService;

    public static ExecutorService service = Executors.newSingleThreadExecutor();

    /**
     * 执行集成测试
     *
     * @param id
     * @param executeCount    执行次数
     * @param concurrentCount 并发数
     * @return
     */
    @Transactional
    public boolean execute(String id, int executeCount, int concurrentCount) {
        // 查询集成测试数据
        IntegrationTest it = selectByPrimaryKey(id);
        if (it == null) {
            throw new NullPointerException("集成测试不可为空");
        }
        List<IntegrationTestInterface> interfaces = it.getInterfaces();
        if (interfaces != null) {
            // 准备集成测试所需参数及服务
            IntegrationTestParams params = new IntegrationTestParams();
            // 回调总数
            AtomicInteger concurrentIntegrationTestCallbackTotalCounter = new AtomicInteger(0);
            // 成功总数
            AtomicInteger concurrentIntegrationTestSuccessTotalCounter = new AtomicInteger(0);
            params.setConcurrentIntegrationTestCallbackTotalCounter(concurrentIntegrationTestCallbackTotalCounter);
            params.setConcurrentIntegrationTestSuccessTotalCounter(concurrentIntegrationTestSuccessTotalCounter);
            params.setItId(id);
            params.setItrId(UUID.randomUUID().toString());
            params.setExecuteCount(executeCount);
            params.setConcurrentCount(concurrentCount);
            params.setInterfaces(interfaces);
            params.setTotalCount(getConcurrentTotalCount(executeCount, concurrentCount, interfaces));
            params.setInterfaceTestService(interfaceTestService);
            params.setIntegrationTestRecordService(recordService);
            params.setIntegrationTestInterfaceUseCaseRecordAssoService(recordAssoService);
            params.setInterfaceRecordService(interfaceRecordService);
            params.setAssertService(assertService);
            params.setTestUseCaseRecordService(testUseCaseRecordService);
            // 异步执行集成测试
            service.submit(new IntegrationTester(params));
        }
        return true;
    }

    private int getConcurrentTotalCount(int executeCount, int concurrentCount, List<IntegrationTestInterface> interfaces) {
        int count = 0;
        // 计算集成测试总并发数
        for (int i = 0; i < executeCount; i++) {
            for (IntegrationTestInterface inter : interfaces) {
                // 每个接口叠加 用例数 * 并发数  = 集成测试总并发数
                count += inter.getAssos().size() * concurrentCount;
            }
        }
        return count;
    }

    private void updateTrigger(IntegrationTestModify params) throws Exception {

        //删除所有trigger
        triggerMapper.deleteByFkId(params.getId());

        //insert Trigger
        for (IntegrationTestTriggerParams trigger : params.getIntegrationTestTriggerParams()) {
            InvokeTrigger invokeTrigger = new InvokeTrigger();
            invokeTrigger.setCount(trigger.getCount());
            invokeTrigger.setEndDatetime(DateUtil.formatStr(trigger.getEndTime()));
            invokeTrigger.setInterval(trigger.getInterval());
            invokeTrigger.setInvokeTriggerId(UUID.randomUUID().toString());
            invokeTrigger.setStartDatetime(DateUtil.formatStr(trigger.getStartTime()));
            invokeTrigger.setFkId(params.getId());
            invokeTrigger.setParams("[{\"key\":\"id\",\"value\":\"" + params.getId() + "\"},"
            		+ "{\"key\":\"executeCount\",\"value\":" + trigger.getExecuteCount() + "},"
            		+ "{\"key\":\"concurrentCount\",\"value\":" + trigger.getConcurrentCount() + "}]");
            String uuid = UUID.randomUUID().toString();
            if (trigger.isSpecial()) {
                invokeTrigger.setTriggerName("#" + uuid);
            } else {
                invokeTrigger.setTriggerName(uuid);
            }
            triggerMapper.insertSelective(invokeTrigger);

            //更新quartz的trigger
            IntegrationTest integrationTest = mapper.selectByPrimaryKey(params.getId());
            UpdateTrigger update = new UpdateTrigger();
            update.setClassName(integrationTest.getClassName());
            update.setMethodName(integrationTest.getMethodName());
            update.setConcurrent(integrationTest.getConcurrent());
            update.setInvokeTrigger(invokeTrigger);
            jobService.updateTriggerKey(update);
        }


    }
}
